/**
 * 认证系统核心工具类
 */
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { randomBytes } from 'crypto';
import { User, Session } from './db/auth-schema';
import { getDb } from './db';

// JWT密钥配置
const JWT_SECRET = process.env.JWT_SECRET || 'aigc-edu-jwt-secret-key-2024';
const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '7d';
const REFRESH_TOKEN_EXPIRES_IN = process.env.REFRESH_TOKEN_EXPIRES_IN || '30d';

// 密码复杂度配置
const PASSWORD_MIN_LENGTH = 6;
const BCRYPT_ROUNDS = 12;

// 登录尝试限制
const MAX_LOGIN_ATTEMPTS = 5;
const LOCKOUT_TIME = 15 * 60 * 1000; // 15分钟

export interface AuthResult {
  success: boolean;
  message: string;
  user?: Partial<User>;
  token?: string;
  refreshToken?: string;
}

export interface TokenPayload {
  userId: string;
  email: string;
  role: string;
  iat?: number;
  exp?: number;
}

/**
 * 认证服务类
 */
export class AuthService {
  /**
   * 用户注册
   */
  static async register(userData: {
    username: string;
    email: string;
    password: string;
    displayName?: string;
  }): Promise<AuthResult> {
    try {
      const db = await getDb();
      const { username, email, password, displayName } = userData;

      // 验证输入数据
      const validation = this.validateRegistrationData(userData);
      if (!validation.valid) {
        return { success: false, message: validation.message };
      }

      // 检查用户是否已存在
      const existingUser = await db.findOne<User>('users', {
        $or: [{ email }, { username }]
      });

      if (existingUser.success && existingUser.data) {
        const field = existingUser.data.email === email ? '邮箱' : '用户名';
        return { success: false, message: `${field}已被注册` };
      }

      // 加密密码
      const passwordHash = await bcrypt.hash(password, BCRYPT_ROUNDS);
      const userId = `user_${Date.now()}_${randomBytes(4).toString('hex')}`;
      const now = new Date().toISOString();

      // 创建用户
      const newUser: Partial<User> = {
        id: userId,
        username,
        email,
        password_hash: passwordHash,
        role: 'user',
        display_name: displayName || username,
        status: 'active',
        email_verified: false,
        login_count: 0,
        created_at: now,
        updated_at: now
      };

      const createResult = await db.create<User>('users', newUser);
      if (!createResult.success) {
        return { success: false, message: '用户创建失败' };
      }

      // 创建用户资料
      await db.create('user_profiles', {
        user_id: userId,
        preferences: JSON.stringify({ theme: 'dark', language: 'zh-CN' }),
        created_at: now,
        updated_at: now
      });

      // 生成token
      const { token, refreshToken } = await this.generateTokens({
        userId,
        email,
        role: 'user'
      });

      // 创建会话
      await this.createSession(userId, token, refreshToken);

      return {
        success: true,
        message: '注册成功',
        user: {
          id: userId,
          username,
          email,
          display_name: displayName || username,
          role: 'user'
        },
        token,
        refreshToken
      };
    } catch (error) {
      console.error('注册失败:', error);
      return { success: false, message: '注册失败，请稍后重试' };
    }
  }

  /**
   * 用户登录
   */
  static async login(credentials: {
    emailOrUsername: string;
    password: string;
    ipAddress?: string;
    userAgent?: string;
  }): Promise<AuthResult> {
    try {
      const db = await getDb();
      const { emailOrUsername, password, ipAddress, userAgent } = credentials;

      // 检查登录尝试次数
      const canLogin = await this.checkLoginAttempts(emailOrUsername, ipAddress);
      if (!canLogin) {
        return { success: false, message: '登录尝试次数过多，请15分钟后重试' };
      }

      // 判断输入的是邮箱还是用户名
      const isEmail = emailOrUsername.includes('@');
      const query = isEmail ? { email: emailOrUsername } : { username: emailOrUsername };

      // 查找用户
      const userResult = await db.findOne<User>('users', query);
      if (!userResult.success || !userResult.data) {
        await this.recordLoginAttempt(emailOrUsername, ipAddress || '', false, '用户不存在');
        return { success: false, message: '邮箱/用户名或密码错误' };
      }

      const user = userResult.data;

      // 检查用户状态
      if (user.status !== 'active') {
        await this.recordLoginAttempt(emailOrUsername, ipAddress || '', false, '账户已被禁用');
        return { success: false, message: '账户已被禁用，请联系管理员' };
      }

      // 验证密码
      const isPasswordValid = await bcrypt.compare(password, user.password_hash);
      if (!isPasswordValid) {
        await this.recordLoginAttempt(emailOrUsername, ipAddress || '', false, '密码错误');
        return { success: false, message: '邮箱/用户名或密码错误' };
      }

      // 生成token
      const { token, refreshToken } = await this.generateTokens({
        userId: user.id,
        email: user.email,
        role: user.role
      });

      // 创建会话
      await this.createSession(user.id, token, refreshToken, ipAddress, userAgent);

      // 更新用户登录信息
      await db.update<User>('users', { id: user.id }, {
        last_login: new Date().toISOString(),
        login_count: user.login_count + 1,
        updated_at: new Date().toISOString()
      });

      // 记录成功登录
      await this.recordLoginAttempt(emailOrUsername, ipAddress || '', true);

      return {
        success: true,
        message: '登录成功',
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          display_name: user.display_name,
          role: user.role,
          avatar: user.avatar
        },
        token,
        refreshToken
      };
    } catch (error) {
      console.error('登录失败:', error);
      return { success: false, message: '登录失败，请稍后重试' };
    }
  }

  /**
   * 验证token
   */
  static async verifyToken(token: string): Promise<{ valid: boolean; payload?: TokenPayload; message?: string }> {
    try {
      const payload = jwt.verify(token, JWT_SECRET) as TokenPayload;
      
      // 检查会话是否存在且有效
      const db = await getDb();
      const sessionResult = await db.findOne<Session>('sessions', { token });
      
      if (!sessionResult.success || !sessionResult.data) {
        return { valid: false, message: '会话不存在' };
      }

      const session = sessionResult.data;
      if (new Date(session.expires_at) < new Date()) {
        // 清理过期会话
        await db.delete('sessions', { token });
        return { valid: false, message: '会话已过期' };
      }

      return { valid: true, payload };
    } catch (error) {
      if (error instanceof jwt.TokenExpiredError) {
        return { valid: false, message: 'Token已过期' };
      }
      if (error instanceof jwt.JsonWebTokenError) {
        return { valid: false, message: 'Token无效' };
      }
      return { valid: false, message: 'Token验证失败' };
    }
  }

  /**
   * 刷新token
   */
  static async refreshToken(refreshToken: string): Promise<AuthResult> {
    try {
      const db = await getDb();
      
      // 查找会话
      const sessionResult = await db.findOne<Session>('sessions', { refresh_token: refreshToken });
      if (!sessionResult.success || !sessionResult.data) {
        return { success: false, message: '刷新token无效' };
      }

      const session = sessionResult.data;
      if (new Date(session.expires_at) < new Date()) {
        await db.delete('sessions', { id: session.id });
        return { success: false, message: '刷新token已过期' };
      }

      // 获取用户信息
      const userResult = await db.findOne<User>('users', { id: session.user_id });
      if (!userResult.success || !userResult.data) {
        return { success: false, message: '用户不存在' };
      }

      const user = userResult.data;
      
      // 生成新token
      const { token: newToken, refreshToken: newRefreshToken } = await this.generateTokens({
        userId: user.id,
        email: user.email,
        role: user.role
      });

      // 更新会话
      await db.update<Session>('sessions', { id: session.id }, {
        token: newToken,
        refresh_token: newRefreshToken,
        expires_at: new Date(Date.now() + this.parseTimeToMs(REFRESH_TOKEN_EXPIRES_IN)).toISOString(),
        updated_at: new Date().toISOString()
      });

      return {
        success: true,
        message: 'Token刷新成功',
        token: newToken,
        refreshToken: newRefreshToken
      };
    } catch (error) {
      console.error('Token刷新失败:', error);
      return { success: false, message: 'Token刷新失败' };
    }
  }

  /**
   * 用户登出
   */
  static async logout(token: string): Promise<{ success: boolean; message: string }> {
    try {
      const db = await getDb();
      await db.delete('sessions', { token });
      return { success: true, message: '登出成功' };
    } catch (error) {
      console.error('登出失败:', error);
      return { success: false, message: '登出失败' };
    }
  }

  /**
   * 生成JWT token和刷新token
   */
  private static async generateTokens(payload: Omit<TokenPayload, 'iat' | 'exp'>): Promise<{
    token: string;
    refreshToken: string;
  }> {
    const token = jwt.sign(payload as object, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN } as jwt.SignOptions);
    const refreshToken = randomBytes(32).toString('hex');
    return { token, refreshToken };
  }

  /**
   * 创建会话记录
   */
  private static async createSession(
    userId: string,
    token: string,
    refreshToken: string,
    ipAddress?: string,
    userAgent?: string
  ): Promise<void> {
    const db = await getDb();
    const sessionId = `session_${Date.now()}_${randomBytes(4).toString('hex')}`;
    const now = new Date().toISOString();
    const expiresAt = new Date(Date.now() + this.parseTimeToMs(REFRESH_TOKEN_EXPIRES_IN)).toISOString();

    await db.create<Session>('sessions', {
      id: sessionId,
      user_id: userId,
      token,
      refresh_token: refreshToken,
      expires_at: expiresAt,
      ip_address: ipAddress,
      user_agent: userAgent,
      created_at: now,
      updated_at: now
    });
  }

  /**
   * 验证注册数据
   */
  private static validateRegistrationData(data: {
    username: string;
    email: string;
    password: string;
  }): { valid: boolean; message: string } {
    const { username, email, password } = data;

    if (!username || username.length < 2) {
      return { valid: false, message: '用户名至少需要2个字符' };
    }

    if (!/^[a-zA-Z0-9_\u4e00-\u9fa5]+$/.test(username)) {
      return { valid: false, message: '用户名只能包含字母、数字、下划线和中文' };
    }

    if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      return { valid: false, message: '请输入有效的邮箱地址' };
    }

    if (!password || password.length < PASSWORD_MIN_LENGTH) {
      return { valid: false, message: `密码至少需要${PASSWORD_MIN_LENGTH}个字符` };
    }

    return { valid: true, message: '验证通过' };
  }

  /**
   * 检查登录尝试次数
   */
  private static async checkLoginAttempts(email: string, ipAddress?: string): Promise<boolean> {
    try {
      const db = await getDb();
      const cutoffTime = new Date(Date.now() - LOCKOUT_TIME).toISOString();
      
      const attemptsResult = await db.findMany('login_attempts', {
        email,
        success: false,
        created_at: { $gte: cutoffTime }
      });

      if (attemptsResult.success && attemptsResult.data) {
        return attemptsResult.data.length < MAX_LOGIN_ATTEMPTS;
      }
      
      return true;
    } catch (error) {
      console.error('检查登录尝试失败:', error);
      return true; // 出错时允许登录
    }
  }

  /**
   * 记录登录尝试
   */
  private static async recordLoginAttempt(
    email: string,
    ipAddress: string,
    success: boolean,
    errorMessage?: string
  ): Promise<void> {
    try {
      const db = await getDb();
      await db.create('login_attempts', {
        id: `attempt_${Date.now()}_${randomBytes(4).toString('hex')}`,
        email,
        ip_address: ipAddress,
        success,
        error_message: errorMessage,
        created_at: new Date().toISOString()
      });
    } catch (error) {
      console.error('记录登录尝试失败:', error);
    }
  }

  /**
   * 解析时间字符串为毫秒
   */
  private static parseTimeToMs(timeStr: string): number {
    const match = timeStr.match(/(\d+)([smhd])/);
    if (!match) return 7 * 24 * 60 * 60 * 1000; // 默认7天
    
    const [, num, unit] = match;
    const value = parseInt(num);
    
    switch (unit) {
      case 's': return value * 1000;
      case 'm': return value * 60 * 1000;
      case 'h': return value * 60 * 60 * 1000;
      case 'd': return value * 24 * 60 * 60 * 1000;
      default: return 7 * 24 * 60 * 60 * 1000;
    }
  }

  /**
   * 清理过期会话
   */
  static async cleanupExpiredSessions(): Promise<void> {
    try {
      const db = await getDb();
      const now = new Date().toISOString();
      await db.deleteMany('sessions', {
        expires_at: { $lt: now }
      });
    } catch (error) {
      console.error('清理过期会话失败:', error);
    }
  }
}

export default AuthService;